package com.YK.retryer.beanprocessor;

import com.YK.retryer.annotation.YKRetryMethod;
import com.YK.retryer.lister.ResultLister;
import com.YK.retryer.strategy.annotationwrapper.intervalwrapper.AbstractTimeIntervalWrapper;
import com.YK.retryer.strategy.annotationwrapper.resultwrapper.AbstractResultListPredicateWrapper;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @author yuank
 * @version 1.0.0
 * @date 2025/2/27 10:02:34
 */
public class YKRetryBeanProcessor implements BeanPostProcessor, ApplicationContextAware {

    private ApplicationContext applicationContext;

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

        //获取方法注解-具体的重试内容
        Method[] declaredMethods = bean.getClass().getDeclaredMethods();

            /**bean-method级**/
            //对每个方法进行代理生成处理
            for (Method method : declaredMethods) {
                //不论是否内部私有都要用
                if (method.isAnnotationPresent(YKRetryMethod.class)){

                    /**方法注解内容**/
                    YKRetryMethod ykRetryMethod = method.getAnnotation(YKRetryMethod.class);

                    //重试判断逻辑器具指定名
                    String normalResultPredicateName = ykRetryMethod.normalResultPredicateName();
                    String throwResultPredicateName = ykRetryMethod.throwResultPredicateName();
                    //要进行重试的结果或异常
                    String[] result = ykRetryMethod.result();
                    Class<? extends Throwable>[] exceptions = ykRetryMethod.exception();

                    //重试时间间隔器
                    String timeIntervalName = ykRetryMethod.timeIntervalName();
                    //重试间隔时间与次数
                    int time = ykRetryMethod.time();
                    TimeUnit timeUnit = ykRetryMethod.timeUnit();
                    int allTryTimes = ykRetryMethod.maxAttempts();

                    //线程池bean与监听器bean
                    String threadPoolBeanName = ykRetryMethod.async();
                    String listerBeanName = ykRetryMethod.listerName();


                    /**自定义重试判断逻辑器包装**/
                    //普通结果逻辑器
                    final AbstractResultListPredicateWrapper normalResultPredicate =
                            (AbstractResultListPredicateWrapper) applicationContext.getBean(normalResultPredicateName);
                    if (result.length != 0) {
                        this.setNormalResultList(result, normalResultPredicate);}

                    //异常结果逻辑器
                    final AbstractResultListPredicateWrapper throwableResultPredicate =
                            (AbstractResultListPredicateWrapper) applicationContext.getBean(throwResultPredicateName);
                    if (exceptions.length != 0) {
                        this.setThrowableResultList(exceptions, throwableResultPredicate);}

                    /**自定义重试时间间隔器包装**/
                    final AbstractTimeIntervalWrapper timeInterval =
                            (AbstractTimeIntervalWrapper) applicationContext.getBean(timeIntervalName);
                    timeInterval.setTime(time).setTimeUnit(timeUnit).setMaxAttempts(allTryTimes);

                    /**相关bean**/
                    if (threadPoolBeanName.length() != 0) {
                        ExecutorService threadPoolBean = (ExecutorService) applicationContext.getBean(threadPoolBeanName);
                    }
                    if (listerBeanName.length() != 0) {
                        ResultLister listerBean = (ResultLister) applicationContext.getBean(listerBeanName);
                    }
                }



            }

        return bean;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    private AbstractResultListPredicateWrapper setNormalResultList(String[] result,
                                                                   AbstractResultListPredicateWrapper predicate) {
        predicate.wrapper(Arrays.asList(result));
        return predicate;
    }

    private AbstractResultListPredicateWrapper setThrowableResultList(Class<? extends Throwable>[] exceptions,
                                                                      AbstractResultListPredicateWrapper predicate) {
        predicate.wrapper(Arrays.asList(exceptions));
        return predicate;
    }
}